#ifndef MEASURE_SIZE_TRACKER_HANDLE_POSITION_VISITOR_H_
#define MEASURE_SIZE_TRACKER_HANDLE_POSITION_VISITOR_H_

#if defined(_MSC_VER)
#pragma once
#endif

#include "geometry_elements.h"
#include "boost\variant\static_visitor.hpp"

class handle_position_visitor : public boost::static_visitor<point_2>
{
public:

  handle_position_visitor(int handle_id)
    : handle_id_(handle_id)
  {
  }

  ~handle_position_visitor(void)
  {
  }

public:

  point_2 operator()(const point_2& point) const
  {
    BOOST_ASSERT(0==handle_id_);
    return point;
  }

  point_2 operator()(const segment_2& segment) const
  {
    BOOST_ASSERT(0==handle_id_ || 1==handle_id_);
    return (0==handle_id_) ? segment.start : segment.end;
  }

  point_2 operator()(const box_2& box) const
  {
    BOOST_ASSERT(0<=handle_id_ && handle_id_<8);

    switch (handle_id_)
    {
    case 0:
      return box.center_right();

    case 1:
      return box.top_right();

    case 2:
      return box.center_top();

    case 3:
      return box.top_left;

    case 4:
      return box.center_left();

    case 5:
      return box.bottom_left();

    case 6:
      return box.center_bottom();

    case 7:
      return box.bottom_right;

    default:
      return point_2();
    }
  }

  point_2 operator()(const angle_box_2& angle_box) const
  {
    BOOST_ASSERT(0<=handle_id_ && handle_id_<8);

    point_2 center(angle_box.center_x, angle_box.center_y);
    point_2 topLeft     = angle_box.top_left();
    point_2 topRight    = angle_box.top_right();
    point_2 bottomLeft  = angle_box.bottom_left();
    point_2 bottomRight = angle_box.bottom_right();
    point_2 left        = (topLeft+bottomLeft)/2;
    point_2 right       = (topRight+bottomRight)/2;
    point_2 top         = (topLeft+topRight)/2;
    point_2 bottom      = (bottomLeft+bottomRight)/2;
    double hl           = draw_obj::sHandleSize*2.0;

    double hort         = 0;
    double vert         = 0;

    if (angle_box.half_width>0) {
      hort = (hl+angle_box.half_width)/angle_box.half_width;
    }

    if (angle_box.half_height>0) {
      vert = (hl+angle_box.half_height)/angle_box.half_height;
    }

    switch (handle_id_)
    {
    case 0:
      return center.interpolate(right, hort);

    case 1:
      return topRight;

    case 2:
      return center.interpolate(top, vert);

    case 3:
      return topLeft;

    case 4:
      return center.interpolate(left, hort);

    case 5:
      return bottomLeft;

    case 6:
      return center.interpolate(bottom, vert);

    case 7:
      return bottomRight;

    default:
      return point_2();
    }
  }

  point_2 operator()(const ring_2& ring) const
  {
    BOOST_ASSERT(0<=handle_id_ && handle_id_<4);

    switch (handle_id_)
    {
    case 0:
      return point_2(ring.center_x+ring.radius, ring.center_y);

    case 1:
      return point_2(ring.center_x, ring.center_y-ring.radius);

    case 2:
      return point_2(ring.center_x-ring.radius, ring.center_y);

    case 3:
      return point_2(ring.center_x, ring.center_y+ring.radius);

    default:
      return point_2();
    }
  }

  point_2 operator()(const arc_2& arc) const
  {
    BOOST_ASSERT(0<=handle_id_ && handle_id_<2);

    switch (handle_id_)
    {
    case 0:
      return arc.start_point();

    case 1:
      return arc.end_point();

    default:
      return point_2();
    }
  }

  point_2 operator()(const annulus_2& annulus) const
  {
    BOOST_ASSERT(0<=handle_id_ && handle_id_<8);

    switch (handle_id_)
    {
    case 0:
      return point_2(annulus.center_x + annulus.start_radius, annulus.center_y);

    case 1:
      return point_2(annulus.center_x, annulus.center_y - annulus.start_radius);

    case 2:
      return point_2(annulus.center_x - annulus.start_radius, annulus.center_y);

    case 3:
      return point_2(annulus.center_x, annulus.center_y + annulus.start_radius);

    case 4:
      return point_2(annulus.center_x + annulus.end_radius, annulus.center_y);

    case 5:
      return point_2(annulus.center_x, annulus.center_y - annulus.end_radius);

    case 6:
      return point_2(annulus.center_x - annulus.end_radius, annulus.center_y);

    case 7:
      return point_2(annulus.center_x, annulus.center_y + annulus.end_radius);

    default:
      return point_2();
    }
  }

  point_2 operator()(const annulus_arc_2& annulus_sector) const
  {
    BOOST_ASSERT(0<=handle_id_ && handle_id_<4);

    switch (handle_id_)
    {
    case 0:
      return annulus_sector.start_start_point();

    case 1:
      return annulus_sector.end_start_point();

    case 2:
      return annulus_sector.start_end_point();

    case 3:
      return annulus_sector.end_end_point();

    default:
      return point_2();
    }
  }

  point_2 operator()(const polygon_2& polygon) const
  {
    BOOST_ASSERT(0<=handle_id_ && handle_id_<polygon.num_points());

    return polygon.at(handle_id_);
  }

private:
  int handle_id_;
};

#endif // MEASURE_SIZE_TRACKER_HANDLE_POSITION_VISITOR_H_